﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.IO;
using AddIn.EA.Core.Interface;
using AddIn.EA.Core.Common;

namespace AddIn.EA.Core.Management
{
    internal class AddInManager
    {
        #region Fields
        private static AddInManager manager;
        private bool _validating = false;
        private bool _hasError = false;
        #endregion

        #region Constructors
        private AddInManager() 
        {
        }
        #endregion

        #region Static Methods
        public static AddInManager Initialize()
        {
            return Initialize(false);
        }
        public static AddInManager Initialize(bool reset)
        {
            if (reset)
                manager = null;

            if (manager != null)
                return manager;

            AddInManager _manager = new AddInManager();
            _manager.Load();

            manager = _manager;

            return manager;
        }
        #endregion

        #region Properties
        public IAddInItem Root { get; private set; }
        #endregion

        #region Public Methods
        public void Load()
        {
            this.Root = null;

            IAddInItem addInItem = new AddInItem(string.Empty);

            LoadFiles(AddInRegistryResources.GetMenuFolder(), ref addInItem);

            this.Root = addInItem;
        }
        #endregion

        #region Private Methods
        private void LoadFiles(string directory, ref IAddInItem parent)
        {
            string[] files = Directory.GetFiles(directory, "*.xml", SearchOption.TopDirectoryOnly);

            foreach (string file in files)
                LoadAddIn(file, ref parent);

            LoadDirectories(directory, ref parent);
        }

        private void LoadDirectories(string directory, ref IAddInItem parent)
        {
            if (Directory.Exists(directory))
            {
                foreach (string childDirectory in Directory.GetDirectories(directory))
                {
                    DirectoryInfo directoryInto = new DirectoryInfo(childDirectory);
                    IAddInItem addInItem = new AddInItem(directoryInto.Name);
                    LoadFiles(childDirectory, ref addInItem);
                    parent.Add(addInItem);
                }
            }
        }

        private void LoadAddIn(string file, ref IAddInItem parent)
        {
            XmlDocument xml = new XmlDocument();
            xml.Schemas.Add(GetAddInSchema());
            xml.Load(file);

            _validating = true;
            _hasError = false;

            xml.Validate(new ValidationEventHandler(XmlValidation));

            _validating = false;

            if (!_hasError)
            {
                foreach (XmlNode node in xml.DocumentElement.ChildNodes)
                {
                    switch (node.Name)
                    {
                        case "Menu":
                            LoadMenu((XmlElement)node, ref parent);
                            break;
                        case "Items":
                            LoadItems((XmlElement)node, ref parent);
                            break;
                        case "Item":
                            parent.Add(LoadItem((XmlElement)node));
                            break;
                    }
                }
            }
        }

        private void LoadMenu(XmlElement element, ref IAddInItem parent)
        {
            IAddInItem addInItem = new AddInItem(element["Name"].InnerText);

            LoadItems((XmlElement)element["Items"], ref addInItem);

            parent.Add(addInItem);            
        }

        private void LoadItems(XmlElement element, ref IAddInItem parent)
        {
            foreach (XmlElement childElement in element.ChildNodes)
            {
                IAddInItem addInItem = LoadItem(childElement);
                if(addInItem != null)
                    parent.Add(addInItem);
            }
        }

        private IAddInItem LoadItem(XmlElement element)
        {
            IAddInArchitecture addIn;

            try
            {
                addIn = (IAddInArchitecture)Activator.CreateInstance(Type.GetType(element.Attributes["assembly"].Value));
            }
            catch { addIn = null; }

            if (addIn != null)
                return new AddInItem(element.Attributes["itemName"].Value, addIn);
            else
                return null;
        }

        private XmlSchema GetAddInSchema()
        {
            XmlSchema schema = null;

            using (StreamReader reader = new StreamReader(AddInRegistryResources.GetSchemaFile(), UTF32Encoding.UTF8))
            {
                schema = XmlSchema.Read(reader, new ValidationEventHandler(XmlValidation));
                reader.Close();
            }

            return schema;
        }

        private void XmlValidation(object sender, ValidationEventArgs e)
        {
            if (_validating)
                _hasError = true;
            else
                new InvalidOperationException(string.Format("Error loading menu schema configuration!\n\n{0}", e.Message));
        }
        #endregion
    }
}

